# 工具类
import json
from re import M
# 邮件包
import smtplib
# 二进制发送文件
from email.mime.text import MIMEText
# 格式化邮件地址
from email.utils import formataddr
# 支持多人发送
from email.mime.multipart import MIMEMultipart
import time


from test_queue import DelayRedisQueue


# 协程库  原生库
import asyncio

import jwt, datetime

# 导入短信模块
from qcloudsms_py import SmsSingleSender

# 导入腾讯云指定的异常
from qcloudsms_py.httpclient import HTTPError

# ssl证书(安全等级更高)
import ssl

import datetime

from werkzeug.datastructures import FileMultiDict

# 设置证书(将短信证书验证)
ssl._create_default_https_context = ssl._create_unverified_context

from db import MyMongo

import random

# python的时区库
import pytz

# 设置时区
tzinfo = pytz.timezone('Asia/Shanghai')

import redis

# 导入mysql操作类
from test_queue import MysqlDb

table_size = 3


drq = DelayRedisQueue('muqyeue')



# 缓存清理接口
def clear_last(id):

    r = redis.Redis(decode_responses=True)

    # 获取当前用户分页缓存
    try:
        keys = r.keys('last%s*' % id)
        # 删除缓存
        r.delete(*keys)
        # print('二次删除ok')
    except Exception as e:
        pass

# clear_last(114)


# 过滤恶意字符
def sql_filter(item):
    # 声明恶意字符黑名单
    black_list = ["\"", "\\", "#", ";", "%", '$']
    for i in black_list:
        item = item.replace(i, '')

    return item

# 生成订单号
def get_order_code():
    order_no = datetime.datetime.now().strftime('%Y%m%d%H%M%S%f')[:-3]
    # order_no = str(time.strftime('%Y%m%d%H%M%S%F',time.localtime(time.time())))

    # 取模运算
    table_num = hash(int(order_no)) % table_size
    # print(table_num + 1)

    return order_no, table_num + 1


# print(get_order_code())

# 订单生成队列
class OrderQueue:

    def __init__(self, key):
        self.key = key
        self.r = redis.Redis(decode_responses=True)
        self.mydb = MysqlDb()

    # 入队
    def push(self, item):
        # 当数据有需求的修改的时候用list,当数据不能修改的时候用tuple

        self.r.rpush(self.key, item)

    # 出队
    def out(self):
        orderno, table_num = get_order_code()

        try:
            uid, price = eval(self.r.lpop(self.key))

        except Exception as e:
            return '订单队列为空'

        # mysql格式日期
        dt = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')

        # num = int(orderno) & 1

        # 查询
        # res = self.mydb.read("SELECT orderid FROM `order_2` union select orderid from `order_1` ORDER BY orderid desc LIMIT 1")

        # 查询当前数据库中所有order_类型的表
        reslist = "select table_name from information_schema.tables where table_schema='mytronadox' and table_name like 'order_%'"

        c = self.mydb.read_list(reslist)

        str1 = ''
        # 重组联合查询的sql语句
        for i in c:
            str1 += 'select orderid from `%s` union ' % i['TABLE_NAME']

        str1 = str1.rstrip('union ') + 'order by orderid desc limit 1'

        # sql
        # print(str1)

        res = self.mydb.read(str1)

        if res and int(orderno) < res['orderid']:
            return '系统时间有问题,订单生成失败'

        self.mydb.update("insert into `order_%s` (`orderid`,`uid`,`price`,`date`) values (%s,%s,%s,'%s')" % (
        table_num, orderno, uid, price, dt))


oq = OrderQueue('order')
strs = (1, 2)
#
# print(type((str(strs))))
oq.push(str(strs))
oq.out()


# import threading
#
# threads = []
#
# for i in range(500):
#
#     threads.append(threading.Thread(target=get_order_code))
#
# [i.start() for i in threads]
# [i.join() for i in threads]

# 封装ACL权限类
class ACL:
    def __init__(self):
        self.mydb = MysqlDb()

    # 动态增加节点
    def add_node(self, name):
        self.mydb.update(" insert into `node` (`name`) values ('%s') " % name)

    # 获取节点
    def get_node(self, nodeid=None):
        if nodeid is None:
            return self.mydb.read_list('select * from `node`')
        else:
            return self.mydb.read("select * from `node` where id = %s" % nodeid)

    # 修改节点
    def edit_node(self, nodeid, name):

        self.mydb.update("update `node` set `name` = '%s' where id = %s" % (name, nodeid))

    # 删除节点
    def del_node(self, nodeid):

        self.mydb.update("delete from `node` where id = %s " % nodeid)

        self.mydb.update("delete from `node_user` where nodeid = %s " % nodeid)

    # 授权(多对多插入数据)
    def set_node(self, nodeid, uid):

        try:
            self.mydb.update("insert into `node_user` (`uid`,`nodeid`) values (%s,%s)" % (uid, nodeid))
        except Exception as e:
            pass

    # 授权所有权限
    def set_allnode(self, uid):

        nodelist = self.get_node()
        for i in nodelist:
            self.set_node(i['id'], uid)

    # 取消权限
    def del_auth(self, uid, nodeid):
        self.mydb.update("delete from `node_user` where uid = %s and nodeid = %s " % (uid, nodeid))

    # 展示当前用户可用权限
    def get_usernode(self, uid):
        # a node表    b node_user表
        return self.mydb.read_list(
            "select a.name,a.id from `node` a left join `node_user` b on a.id = b.nodeid where b.uid = %s" % uid)

    # 针对某个节点查看当前用户是否有权限
    def get_nodeuser(self, nodeid, uid):

        res = self.mydb.read_list('select * from `node_user` where uid = %s and nodeid = %s ' % (uid, nodeid))

        if res:
            return True
        else:
            return False


# acl = ACL()
# acl.del_auth(1, 2)
# print(acl.get_nodeuser(4, 1))
# acl.del_node(4)
# acl.edit_node(4,'重置密码')
# print(acl.get_node(4))

# 基于角色rbac  权限模型
class RBAC(ACL):

    # 重写授权
    def set_node(self, nodeid, roleid):
        try:
            self.mydb.update("insert into `node_user` (`uid`,`nodeid`) values (%s,%s)" % (roleid, nodeid))
        except Exception as e:
            pass

    # 重写取消权限
    def del_auth(self, roleid, nodeid):
        self.mydb.update("delete from `node_user` where uid = %s and nodeid = %s " % (roleid, nodeid))

    # 重写展示当前用户可用权限
    def get_usernode(self, uid):
        # a user表    b role表   c node_user表  d node表

        return self.mydb.read_list(
            "select a.username,b.id as roleid,d.id,b.name as rolename,d.name from `user` a left join `role` b on "
            "a.type = b.id left join node_user c on b.id = c.uid left join node as d on c.nodeid = d.id "
            "where a.id = %s" % uid)

    # 针对某个节点查看当前用户是否有权限
    def get_nodeuser(self, nodeid, uid):

        res = self.mydb.read_list("select a.username,b.id as roleid,c.nodeid from `user` a left join `role` b on "
                                  "a.type = b.id left join node_user c on b.id = c.uid where a.id = %s and c.nodeid = %s" % (
                                      uid, nodeid))

        if res:
            return True
        else:
            return False

    # 通过资源路径 -->查询 -->节点id
    def get_nodeid_uri(self, uri):
        # a uri表  查  uri  id
        # b  node_uri  查  node_id
        res = self.mydb.read_list(
            "select b.nodeid from uri a left join node_uri b on a.id = b.urlid where a.url = '%s'" % uri)
        return res


# rbac = RBAC()


# print(rbac.get_nodeid_uri('/adupload/'))
# print(rbac.get_nodeuser(2, 1))


# 封装ABAC权限模型------辅助,集合rbac,acl使用
class ABAC:

    def __init__(self):
        self.r = redis.Redis(decode_responses=True)

    # 设置权限
    def set_role(self, roleid, key, val):

        try:
            self.r.hset(roleid, key, val)
        except Exception as e:
            self.r.delete(roleid)
            self.r.hset(roleid, key, val)

    # 取消权限
    def del_role(self, roleid, key):

        self.r.hdel(roleid, key)

    # 获取所有当前角色的属性权限
    def get_role(self, roleid):

        return self.r.hgetall(roleid)


abac = ABAC()


# 设置角色 1   设置字段  sex 0
# abac.set_role('1', 'age', '[10,20]')
# abac.set_role('1', 'sex',0)
# print(abac.get_role('1'))

# 二进制权限模型
class BinAuth:

    # 初始化
    def __init__(self):
        # 初始化数据库
        self.mydb = MysqlDb()

    # 判断用户对于单节点是否有权限
    def auth_node(self, uid, nodeid):

        # 查询节点功能的二进制码
        node = self.mydb.read("select byte from node where id = %s" % nodeid)

        role = self.mydb.read(" select b.byte from user a left  join role b on a.type = b.id where a.id = %s" % uid)

        # 位与运算
        res = node['byte'] & role['byte']

        return res

    # 授权操作
    def set_node(self, roleid, nodeid):
        # 查询节点功能的二进制码
        node = self.mydb.read("select byte from node where id = %s" % nodeid)

        # 查询当前角色的二进制码
        role = self.mydb.read("select byte from role where id = %s" % roleid)

        # 位或运算
        byte = role['byte'] | node['byte']

        # 修改数据库写入
        self.mydb.update("update role set byte = %s where id = %s" % (byte, roleid))

    # 取消授权操作
    def del_node(self, roleid, nodeid):
        # 查询节点功能的二进制码
        node = self.mydb.read("select byte from node where id = %s" % nodeid)

        # 查询当前角色的二进制码
        role = self.mydb.read("select byte from role where id = %s" % roleid)

        # 位与运算  判断是否有权限 &
        res = role['byte'] & node['byte']
        # 如果有, 取消, 否则不判断的话会添加进去
        if res:
            # 位异或运算
            byte = role['byte'] ^ node['byte']

            # 修改数据库写入
            self.mydb.update("update role set byte = %s where id = %s" % (byte, roleid))

    # 获取用户权限(多对多)
    def get_usernode_m2m(self, uid):
        # 查询角色
        # 用户表  user        a
        # 关系表  user_role   b
        # role表  role        c
        roles = self.mydb.read_list(
            "select c.byte,c.id,c.name from user a left join user_role b on a.id = b.uid left join role c "
            "on b.roleid = c.id where a.id = %s" % uid)

        # 查询所有节点
        nodes = self.mydb.read_list("select *  from node")

        type = 0

        # 遍历功能节点
        for i in roles:
            type = type | i['byte']

        nlist = [i for i in nodes if type & i['byte']]

        return nlist

    # 获取用户角色已有的权限(一对多)
    def get_usernode(self, uid):

        # 查询角色的二进制码
        role = self.mydb.read("select b.byte from user a left join role b on a.type = b.id where a.id = %s" % uid)

        # 查询所有节点
        nodes = self.mydb.read_list("select *  from node")

        nlist = []
        # 遍历功能节点
        for i in nodes:

            # 位于操作
            res = role['byte'] & i['byte']

            if res:
                nlist.append(i)

        # print(nlist)

        return nlist


binauth = BinAuth()


# print(binauth.get_usernode_m2m(27))

# print(binauth.auth_node(1, 2))

# binauth.get_usernode(1)

# binauth.del_node(1, 2)

# binauth.set_node(1, 2)

# binauth.get_usernode(1)


class Chat:
    def __init__(self):

        # decode_responses 自动转码
        self.r = redis.Redis(decode_responses=True)

    # 入队操作
    def push(self, key, meg):

        if self.r.exists(key) and meg != "":
            self.r.lpush(key, meg)
        else:
            self.r.lpush(key, meg)
            self.r.expire(key, 60 * 60 * 3)

    # 获取所有聊天记录
    def get(self, key):
        return self.r.lrange(key, 0, -1)

    # 出队              # 超时
    def pop(self, timout=None):
        item = self.r.blpop(timout)

        # 返回
        return item


# 图集类
class ImgList:

    def __init__(self):
        self.mymongo = MyMongo('img')

    # 删除图集
    def del_img(self, sid):
        self.mymongo.remove({'sid': sid})

    # 获取图集
    def get_img(self, sid):
        res = self.mymongo.find_one({'sid': sid})

        if res is None:
            return []
        return res['imglist']

    # 修改图集
    def update_one(self, sid, imglist):
        self.mymongo.update({'sid': sid}, 'imglist', imglist)

    # 插入图集
    def insert_one(self, sid, imglist):
        self.mymongo.insert({'sid': sid, 'imglist': imglist})


# imglist = ImgList()
# print(imglist.get_img(3))
# print(imglist.update_one(3,['http://localhost:8888/static/2.jpg']))
# print(imglist.insert_one(2,['http://localhost:8888/static/2.jpg','http://localhost:8888/static/2.jpg']))


# 用户关注类
class Flow:
    # 初始化
    def __init__(self):
        # 初始化数据连接
        self.r = redis.Redis(decode_responses=True, charset='utf-8', encoding='utf-8')

    # 查看是否有关注
    def ifflow(self, uid, fuid):
        res = self.r.hexists(uid, fuid)
        return res

    # 设置关注
    def set_flow(self, uid, fuid, fusername):
        res = self.r.hset(uid, fuid, fusername)
        return res

    # 取消关注
    def del_flow(self, uid, fuid):
        self.r.hdel(uid, fuid)

    # 获取关注人id
    def get_flow(self, uid):
        return self.r.hkeys(uid)

    # 获取关注人用户名
    def get_username(self, uid):
        return self.r.hvals(uid)


flow = Flow()


# print(flow.set_flow(50,500,111))
# print(flow.get_flow(50))
# print(flow.set_flow('23832','23831',13))
# print(flow.ifflow('23832','23831'))
# print(flow.get_username(50))


# 发送短信类
class SendSms:

    def __init__(self):
        # 应用id   appid
        self.appid = 1400590057

        # 应用秘钥
        # self.appkey = 'bfa928250db600e75cb596f6319b32e1'
        self.appkey = 'bfa928250db600e75cb596f6319b32e1'

        # 短信模板
        self.template_id = 1175459

        # 短信签名
        self.sms_sign = 'python面试总结'

    def get_code(self):
        mystr = ''
        for i in range(4):
            mystr += str(random.randint(0, 9))
        return mystr

    def send_sms(self, phone):
        # 实例化对象
        mysms = SmsSingleSender(self.appid, self.appkey)

        # 定义通配符模板本身的变量

        code = self.get_code()

        parmas = [code]
        mymongo = MyMongo()
        mymongo.insert({'datetime': tzinfo.localize(datetime.datetime.now()), 'code': code, 'phone': phone})
        # try:
        #     mysms.send_with_param(86,phone,self.template_id,parmas,sign=self.sms_sign,extend='',ext='')
        # except HTTPError as e:
        #     print(str(e))
        # except Exception as e:
        #     print(str(e))


# # 实例化对象
# sendsms = SendSms()

# 调用
# sendsms.send_sms('17744577183')


# 令牌方法 token

class MyJwt:

    # 加密方法
    def encode(self, userinfo):

        encode_str = jwt.encode(userinfo, 'Dragon', algorithm='HS256')

        try:
            encode_str = str(encode_str, 'utf-8')
        except Exception as e:
            pass

        return encode_str

    # 加密密钥   添加生命周期
    def encode_jwt_time(self, userinfo, mytime):

        # 单独控制载荷信息
        palyload = {
            # 设置过期时间
            'exp': int((datetime.datetime.now() + datetime.timedelta(seconds=mytime)).timestamp()),
            # 用户信息
            'data': userinfo
        }
        # 用户信息    密钥       算法
        mystr = jwt.encode(palyload, 'Dragon', algorithm='HS256')

        try:
            mystr = str(mystr, 'utf-8')
        except Exception as e:
            pass

        return mystr

    # 解密
    def decode(self, jwt_str):

        return jwt.decode(jwt_str, 'Dragon', algorithms=['HS256'])


# myjwt = MyJwt()

# print(myjwt.encode({'id':16}))
# print(myjwt.encode_jwt_time({'id':16},30))
# print(myjwt.decode('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MTZ9.6G6QX2v3WSBO7ho6fu_YEX3du0avGuFlb1gJO2cCcPo'))


# 封装邮箱发送

async def send_mail(to, title, content, id):
    # print('开始执行')
    # # time.sleep(5)

    # for i in range(5):
    #     print("耗费了%s秒" % i)
    #     time.sleep(1)

    # print('发送邮件')

    # return '发送邮件成功'

    # 发送者
    # _user = '你的邮箱地址'
    _user = '2932746380@qq.com'

    # smtp密钥
    # _pwd = '你的邮箱smtp密钥'
    _pwd = 'bxkozzhabqkcdcic'

    # 发送设置
    meg = MIMEMultipart()
    # 标题
    meg['Subject'] = title
    # 来自
    meg['From'] = _user
    # 邮件去往哪里
    meg['To'] = to

    # 加密操作
    myjwt = MyJwt()

    id = myjwt.encode_jwt_time({'id': id}, 60 * 5)

    # 邮件内容                                                                           类型     编码   
    mail = MIMEText("请点击超链接进行激活<a href='http://localhost:8888/emailback/?id=%s'>激  活</a>" % id, 'html', 'utf-8', )
    # 邮件嵌入内容
    meg.attach(mail)

    # 调用
    s = smtplib.SMTP_SSL('smtp.qq.com', 465)

    # 登录腾讯邮箱发送人
    s.login(_user, _pwd)

    # 目标发送接收人
    s.sendmail(_user, to, meg.as_string())

    # 关闭协议
    s.close()


# 调用
# send_mail('2932746380@qq.com','123','123',1)

# 声明事件循环
# loop = asyncio.get_event_loop()
# # 在事件循环对象中调用异步方法   事件中循环,直到执行结束
# res = loop.run_until_complete(send_mail('2932746380@qq.com','123','123',1))
# print(res)

# # 手动关闭事件循环
# loop.close()


def ret_json(mydict):
    # indent 缩进方式
    res = json.dumps(mydict, ensure_ascii=False, default=str, indent=4)

    return res
